home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Experimental BBS Explossion 3
/
Experimental BBS Explossion III.iso
/
c
/
bc_pas_1.zip
/
MIDIA.ASM
< prev
next >
Wrap
Assembly Source File
|
1992-12-08
|
41KB
|
1,770 lines
;#>
;$Author: DCODY $
;$Date: 08 Dec 1992 16:51:22 $
;$Header: X:/sccs/midi/midia.asv 1.7 08 Dec 1992 16:51:22 DCODY $
;$Log: X:/sccs/midi/midia.asv $
;
; Rev 1.7 08 Dec 1992 16:51:22 DCODY
; moved externADDR macro for Borland Link. Also, added valid
; return codes for the init routine, which wasn't returning the
; correct value.
;
; Rev 1.6 23 Sep 1992 10:34:44 DCODY
; changed MVGetH... to mvGetH...
;
; Rev 1.5 04 Sep 1992 16:55:12 DCODY
; NEAR external choked large model programs
;
; Rev 1.4 20 Jul 1992 11:42:44 DCODY
; call to MVGetHWVersion requests active I/O detection. Found some
; I/O that was not relocatable, and now performs XOR relocation.
;
; Rev 1.3 17 Jul 1992 13:57:30 DCODY
; make I/O addresses relocatable.
;
; Rev 1.2 27 Jun 1992 15:44:56 DCODY
; removed debug output code.
;
; Rev 1.1 25 Jun 1992 21:48:46 DCODY
; PAS2 update
;
; Rev 1.0 15 Jun 1992 10:42:58 BCRANE
; Initial revision.
;$Logfile: X:/sccs/midi/midia.asv $
;$Modtimes$
;$Revision: 1.7 $
;$Workfile: midia.asm $
;#<
Title MIDIA.ASM -- Media Vision 3802 Midi Hardware Dependent Module
Subttl Copyright (c) 1991,1992. Media Vision Inc. All Rights Reserved.
page 64,131
; /*\
;---|*|-----------------------====< MIDIA.ASM >====------------------------
;---|*|
;---|*| Low Level MIDI I/O routines for the Pro Audio Spectrum cards.
;---|*|
; \*/
.xlist
include model.inc
include masm.inc
include mvmidi.inc
include common.inc
include target.inc
.list
;
; /*\
;---|*|----====< int mvMIDIEnable >====----
;---|*|
;---|*| mvMIDIEnable - Initialize the MIDI interface
;---|*|
;---|*| Entry Conditions:
;---|*| wParm1 is a bit field for process control
;---|*| D0 = 1 to enable input interrupts
;---|*| D1 = 1 to enable output interrupts
;---|*|
;---|*| Exit Conditions:
;---|*| AX = -1 if cannot init the MIDI hardware.
;---|*|
;---|*|----====< void mvMIDIDisable >====----
;---|*|
;---|*| mxdDisable - MIDI shutdown
;---|*|
;---|*| Entry Conditions
;---|*| None
;---|*|
;---|*| Exit Conditions
;---|*| There is no return value.
;---|*|
;---|*|----====< int mvMIDIGetBuff >====----
;---|*|
;---|*| mvMIDIGetBuff - get data byte from MIDI channel
;---|*|
;---|*| Entry Conditions
;---|*| wParm1 holds the count to move
;---|*| dParm2 points to the target buffer
;---|*|
;---|*| Exit Conditions
;---|*| AX = 0, no data, # of bytes moved
;---|*|
;---|*|----====< int mvMIDIGetByte >====----
;---|*|
;---|*| mvMIDIGetByte - get/check one byte from MIDI channel buffer
;---|*|
;---|*| Entry Conditions
;---|*| None
;---|*|
;---|*| Exit Conditions
;---|*| AX = byte, or -1 if no data
;---|*|
;---|*|----====< void mvMIDISendBuff >====----
;---|*|
;---|*| mvMIDISendBuff - send a buffer of data
;---|*|
;---|*| Entry Conditions
;---|*| wParm1 = integer of count to load
;---|*| dParm1 = far pointer to the buffer
;---|*|
;---|*| Exit Conditions
;---|*| There is no return value.
;---|*|
;---|*|----====< void mvMIDISendByte >====----
;---|*|
;---|*| mvMIDISendByte - send data byte out MIDI channel
;---|*|
;---|*| Entry Conditions
;---|*| wParm1 datum
;---|*|
;---|*| Exit Conditions
;---|*| There is no return value.
;---|*|
; \*/
;
externADDR mvGetHWVersion ; determine the installed hardware type
.data
;
; /*\
;---|*|------------====< Global Data Definitions >====------------
; \*/
;
extrn _MVTranslateCode:word
extrn _MVHWVersionBits:word
;
; The overflow count will always be here, but we won't make it public
; except for debugging purposes
;
ifdef DEBUG
public _overflow
endif
_overflow dw 0 ; overflow count
;
; MidiInFilter allows certain types of MIDI messages to be tossed out...
;
; NOTE: this currently works for active sense ONLY.
;
public _MidiInFilter
_MidiInFilter dw MF_ACTSENSE ; default ignores active sense
;
; /*\
;---|*|------------====< Private Data Definitions >====------------
; \*/
;
;
; This midi code uses interrupt driven I/O, so a circular buffer is used
; to support the data flow.
;
MAXQUEUE equ 128
;
; MIDI input circular buffer
;
iCircularQueue db MAXQUEUE dup (0)
MIDIiQueueCnt dw 0
lpiMIQueue dw iCircularQueue
lpiMOQueue dw iCircularQueue
;
; MIDI output circular buffer
;
oCircularQueue db MAXQUEUE dup (0)
MIDIoQueueCnt dw 0
lpoMIQueue dw oCircularQueue
lpoMOQueue dw oCircularQueue
;
; Interrupt channel number and mask
;
_IRQNumb db -1
_IRQMask db 0
;
; Interrupt Service Routine re-entrancy sema-phore
;
ISRbusy db -1 ; ISR semaphore
;
; Internal process control to handle polled or interrupt driven data I/O
;
ProcessControl db 0 ; Interrupt or polled I/O
INTINPUT equ 00000001b
INTOUTPUT equ 00000010b ; (currently not implemented)
;
; /*\
;---|*|------------====< Yamaha 3802 midi interface variables >====------------
; \*/
;
MDctrl db 0 ; MDSYSCTLR shadow for write only hardware
ISRenab db 0 ; R06 TX and RX int enable bits
; r02/r03/r06 bit definitions
TxIRQ equ 01000000b ; Tx FIFO interrupt
RxIRQ equ 00100000b ; Rx FIFO interrupt
; r34 bit definitions
RxRDY equ 10000000b ; Rx FIFO non-empty
; r54 bit definitions
TxEMP equ 10000000b ; Tx FIFO empty
TxRDY equ 01000000b ; Tx FIFO non-full
TIMER1 equ 2173
;
;------------------------================================----------------------
;------------------------====< MIDI Common Routines >====----------------------
;------------------------================================----------------------
;
.code
assume ds:@data,es:nothing
;
; We will save the old IRQ vector here to guarrantee access at all times.
;
OldISR dd 0 ; holds the original IRQ vector
;
; Toggle the MIDI interrupt to stimulate latent IRQs
;
ToggleMV101mask macro
mov dx,INTRCTLR
xor dx,[_MVTranslateCode]
in al,dx
xor al,bICmidi ; disable MIDI interrupts
out dx,al
pause
xor al,bICmidi ; enable MIDI interrupts
out dx,al
endm
;
; /*\
;---|*|----====< int mvMIDIEnable >====----
;---|*|
;---|*| Initialize the MIDI interface
;---|*|
;---|*| Entry Conditions:
;---|*| wParm1 is a bit field for process control:
;---|*| D0 = 1 to enable input interrupts.
;---|*| D1 = 1 to enable output interrupts.
;---|*|
;---|*| Exit Conditions:
;---|*| AX = -1 if cannot init the MIDI hardware.
;---|*|
; \*/
;
public mvMIDIEnable
mvMIDIEnable proc
push bp ; frame the stack for our 1 parameter
mov bp,sp
;
; reset the MIDI buffering system
;
mov [lpiMIQueue],OFFSET iCircularQueue
mov [lpiMOQueue],OFFSET iCircularQueue
mov [lpoMIQueue],OFFSET oCircularQueue
mov [lpoMOQueue],OFFSET oCircularQueue
mov [MIDIoQueueCnt],0
mov [MIDIiQueueCnt],0
mov [_overflow],0
mov [_MidiInFilter],MF_ACTSENSE
;
; If the hardware bits have not been determined, do it...
;
cmp _MVHWVersionBits,-1 ; version bits = -1 if the function
jnz @F ; hasn't been called yet.
mov ax,USE_ACTIVE_ADDR ; do the call
push ax ; to setup the hardware bits, etc.
call mvGetHWVersion
pop ax
;
@@:
;
; determine the IRQ for the Pro Audio card
;
call whereirq ; looks for MVSOUND.SYS
;
; convert each bit to a full mask. Send this mask to each routine.
;
mov ax,wParm1 ; load the bit flags
mov [ProcessControl],al ; save as our process control
ror ax,1 ; ah holds input bit, al holds output
neg ah
sbb ah,ah ; ah = FF to enable ints, else 0
neg al
sbb al,al ; al = FF to enable ints, else 0
;
; perform the proper init
;
test [_MVHWVersionBits],bMV101; MV101 version of MIDI?
jz @F ; no, go do yamaha
;
; initialize the MV101 MIDI device
;
call enable_mv101
mov ax,-1 ; default to bad
jc mvmienbad ; cannot set it up...
inc ax ; return oky doky...
pop bp
ret
;
@@:
;
; initialize the Yamaha 3802
;
call enable_yamaha
mov ax,-1 ; default to bad
jc mvmienbad ; cannot set it up...
inc ax ; return oky doky...
;
mvmienbad:
pop bp
ret
mvMIDIEnable endp
;
; /*\
;---|*|----====< void mvMIDIDisable >====----
;---|*|
;---|*| MIDI device shutdown.
;---|*|
;---|*| Entry Conditions:
;---|*| None.
;---|*|
;---|*| Exit Conditions:
;---|*| There is no return value.
;---|*|
; \*/
;
public mvMIDIDisable
mvMIDIDisable proc
;
; remove ourselves from the chain
;
call unhook_interrupt
;
; perform the proper init
;
test [_MVHWVersionBits],bMV101; MV101 version of MIDI?
jz @F ; no, go do yamaha
call disable_MV101 ; disable the MV101 MIDI
ret
;
@@:
call disable_yamaha ; disable the Yamaha MIDI
;
midblexit:
ret
mvMIDIDisable endp
;
; /*\
;---|*|----====< int mvMIDIGetBuff >====----
;---|*|
;---|*| Get data byte from MIDI channel.
;---|*|
;---|*| Entry Conditions
;---|*| wParm1 holds the count to move.
;---|*| dParm2 points to the target buffer.
;---|*|
;---|*| Exit Conditions
;---|*| AX = 0, no data, # of bytes moved.
;---|*|
; \*/
;
public mvMIDIGetBuff
mvMIDIGetBuff proc
push bp
mov bp,sp
;
; save the C criticals
;
push es
push si
push di
;
; load the parameters
;
mov cx,wParm1 ; get the number of bytes to move
les di,wParm2 ; get the target pointer
mov si,[lpiMOqueue] ; get the source pointer
sub dx,dx ; dx holds the # of bytes moved
cld
;
; using interrupt driven input?
;
test [ProcessControl],INTINPUT
jz mvigb_20 ; polled, check each h/w
;
mvigb_05:
;
; read as many bytes out of the queues until one is empty, or full.
;
cmp [MIDIiQueueCnt],0 ; all done?
jz mvigb_10 ; yes, return # of bytes moved
movsb
dec [MIDIiQueueCnt] ; one less byte here...
inc dx
cmp si,offset iCircularQueue+MAXQUEUE
jl @F
mov si,offset iCircularQueue
;
@@:
loop mvigb_05 ; get it all...
;
mvigb_10:
mov [lpiMOQueue],si ; save the pointer
mov ax,dx
;
mvigb_ret:
pop di
pop si
pop es
pop bp
ret
;
mvigb_20:
;
; This is a little slow, but avoids messy code duplication
;
push si ; save si
mov si,cx ; si will be our loop counter
mov bp,cx ; bp will hold the total count
;
mvigb_25:
call FFAR ptr mvMIDIGetByte ; get the next byte
cmp ax,-1 ; any data?
jz mvigb_30 ; nope, we're done...
stosb ; save in the callers buffer
dec si ; all done?
jnz mvigb_25 ; nope, continue looping
;
mvigb_30:
sub bp,si ; bp holds the total read count
mov ax,bp
pop si
jmp short mvigb_ret
mvMIDIGetBuff endp
;
; /*\
;---|*|----====< int mvMIDIGetByte >====----
;---|*|
;---|*| Get one byte from MIDI channel buffer.
;---|*|
;---|*| Entry Conditions
;---|*| None.
;---|*|
;---|*| Exit Conditions
;---|*| AX = -1 if no data.
;---|*| AH = 0 if data in AL.
;---|*|
; \*/
;
public mvMIDIGetByte
mvMIDIGetByte proc
;
; If the code is running as interrupt input, then we can check the queue
;
test [ProcessControl],INTINPUT
jz mvgbt_20 ; polled, check each h/w
cmp [MIDIiQueueCnt],0 ; any data?
jz mvgbt_none ; no, just return now
mov bx,[lpiMOqueue] ; get the source pointer
sub ah,ah ; clear out the top
mov al,[bx] ; load the bottom
dec [MIDIiQueueCnt] ; one less byte here...
inc bx
cmp bx,offset iCircularQueue+MAXQUEUE
jl mvgbt_05
mov bx,offset iCircularQueue
;
mvgbt_05:
mov [lpiMOqueue],bx ; save the new pointer
ret
;
mvgbt_none:
mov ax,-1 ; bad, so exit with error flag
ret
;
mvgbt_20:
;
; This is the polled mode of MIDI input - go check the correct chip FIFO
;
test [_MVHWVersionBits],bMV101; MV101 version of MIDI?
jz mvgbt_yamaha ; no, go do yamaha
;
; read the MV101 MIDI fifo
;
mov dx,MIDISTATUS ; check data available
xor dx,[_MVTranslateCode] ; translated I/O addr
in al,dx ; isolate the data available bit
and ax,bMSRififo
jz mvgbt_none ; no data available, go report it...
mov dx,MIDIDATA
xor dx,[_MVTranslateCode] ; translated I/O addr
in al,dx ; return the byte in al, ah = 0
ret
;
mvgbt_yamaha:
;
; read the Yamaha 3802 MIDI fifo.
;
mov al, 3 ; 3x
mov dx, MDSYSCTLR ; set the 3802 global reg set index
mov [MDctrl],al ; shadow...
out dx, al
mov dx, MDGROUP4 ; 34 = FIFO-Rx status
in al, dx
and ax, RxRDY ; RxRDY bit?
jz mvgbt_none ; no data available, go report it...
mov dx, MDGROUP6 ; 36 = FIFO-Rx data
in al, dx ; return the byte in al, ah = 0
ret
mvMIDIGetByte endp
;
; /*\
;---|*|----====< void mvMIDISendByte >====----
;---|*|
;---|*| mvMIDISendByte - send a MIDI data byte out the MIDI channel.
;---|*|
;---|*| Entry Conditions
;---|*| wParm1 - MIDI byte to be sent.
;---|*|
;---|*| Exit Conditions
;---|*| There is no return value.
; \*/
;
public mvMIDISendByte
mvMIDISendByte proc
;
; send the byte out directly if polled output, else queue it up to keep order
;
push bp
mov bp,sp
mov al,wParm1 ; grab the byte
test [ProcessControl],INTOUTPUT ; interrupt output?
jnz mvsnd_ints ; yes, we will queue it...
call dosendbyte ; send it out straight
pop bp
ret
;
mvsnd_ints:
;
; Interrupt driven output requires the byte be queue to keep the byte order.
;
mov bx,[lpoMIQueue] ; get the input-to-buffer pointer
;
@@:
cmp [MIDIoQueueCnt],MAXQUEUE; is the internal queue full?
jnz @F ; no, go load another byte
call primexmit ; make sure the xmitter is working
jmp short @B ; go wait for data to move
;
@@:
mov [bx],al
inc bx
cmp bx,offset oCircularQueue+MAXQUEUE ; wrapped?
jnz @F
mov bx,offset oCircularQueue ; yes, go back to the beginning
;
@@:
mov [lpoMIQueue],bx ; save the new input-to-buffer pointer
inc [MIDIoQueueCnt] ; add one more into the FIFO
call primexmit ; make sure the xmitter is working
pop bp
ret
mvMIDISendByte endp
;
; /*\
;---|*|----====< void mvMIDISendBuff >====----
;---|*|
;---|*| mvMIDISendBuff - send a buffer of data.
;---|*|
;---|*| Entry Conditions
;---|*| wParm1 = integer of count to load.
;---|*| dParm1 = far pointer to the buffer.
;---|*|
;---|*| Exit Conditions
;---|*| Returns when all the bytes are loaded in the circular queue.
; \*/
;
public mvMIDISendBuff
mvMIDISendBuff proc
push bp
mov bp,sp ; 'C' frame
push es ; save the 'C' criticals
push si
;
; we will load the entire block into the FIFO. This even means waiting for the
; transmitter to empty enough data until we're done.
;
mov cx,wParm1 ; get the buffer counter
les si,wParm2 ; get the far pointer
mov bx,[lpoMIQueue] ; get the input-to-buffer pointer
;
queueloop:
cmp [MIDIoQueueCnt],MAXQUEUE; is the internal queue full?
jnz @F ; no, go load another byte
call primexmit ; make sure the xmitter is working
jmp short queueloop
;
@@:
lods byte ptr es:[si] ; get the next MIDI byte
mov [bx],al
inc bx
cmp bx,offset oCircularQueue+MAXQUEUE ; wrapped?
jnz @F
mov bx,offset oCircularQueue ; yes, go back to the beginning
;
@@:
inc [MIDIoQueueCnt] ; add one more into the FIFO
loop queueloop ; load all the bytes into the queue
mov [lpoMIQueue],bx ; save the new input-to-buffer pointer
;
; We've loaded the FIFO. if polling, call the output routine till done.
;
test [ProcessControl],INTOUTPUT ; interrupt driven output?
jnz sendbuffints ; yes, force the xmitter to work
;
; this byte goes out the MV101
;
@@:
call primexmit ; make sure the xmitter is working
cmp [MIDIoQueueCnt],0 ; any more queued up data?
jnz @B ; yes, stay here till sent...
jmp short sendbuffexit ; all done, return home
;
sendbuffints:
;
; make sure the xmitter is primed to generate interrupts
;
call primexmit
;
sendbuffexit:
pop si
pop es
pop bp
ret
mvMIDISendBuff endp
;
;---------------------------==============================---------------------
;---------------------------====< Local Subroutines > ====---------------------
;---------------------------==============================---------------------
;
;
; /*\
;---|*|----====< disable_MV101 >====----
;---|*|
;---|*| This routine will disable any MV101 MIDI
;---|*| interrupts and reset the tranceiver.
;---|*|
;---|*| Entry Conditions:
;---|*| None.
;---|*|
;---|*| Exit Conditions:
;---|*| AX, DX modified
;---|*|
; \*/
;
disable_MV101 proc near
mov dx,MIDICONTROL ; get the control address
xor dx,[_MVTranslateCode] ; translate the address
sub al,al ; flush all enables
out dx,al ; kill fifo irq
ret
disable_MV101 endp
;
; /*\
;---|*|----====< disable_yamaha >====----
;---|*|
;---|*| This routine will disable any Yamaha MIDI
;---|*| interrupts and reset the tranceiver.
;---|*|
;---|*| Entry Conditions:
;---|*| None.
;---|*|
;---|*| Exit Conditions:
;---|*| AX, DX modified
;---|*|
; \*/
;
disable_yamaha proc near
;
; disable interrupts in the yamaha 3802
;
sub ax, ax
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al ; 0x
mov dx, MDGROUP6 ; 06 = IRQ enable request
out dx, al
.errnz MDGROUP6-MDGROUP5-1
dec dx ; 05 = IRQ mode control
out dx, al
ret
disable_yamaha endp
;
; /*\
;---|*|----====< dosendbyte >====----
;---|*|
;---|*| A generic routine to send one byte out to the hardware.
;---|*|
;---|*| Entry Conditions;
;---|*| AL holds the byte to be sent.
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX modified.
;---|*| Byte sent, no return value.
;---|*|
; \*/
;
dosendbyte proc near
;
; save the byte until we need it...
;
mov ah,al ; ah holds the byte to send
;
; select the proper chip.
;
test [_MVHWVersionBits],bMV101; MV101 version of MIDI?
jz send_yamaha ; no, go do yamaha
;
; this byte goes out the MV101
;
mov dx, MIDIFIFOS ; get the transmitter status
xor dx,[_MVTranslateCode] ; translated I/O addr
;
; we will only load up to 15 bytes in the output FIFO since a zero count
; means either 16 bytes free or 0 bytes free. By only loading 15 bytes max,
; we assume a zero means 16 bytes free.
;
in al,dx ; get the FIFO output free room count
and al,bMFCofifo ; isolate the count
jz mvsendit ; 16 bytes free
sub cx,cx ; we will do a timeout
;
@@:
in al, dx ; get the FIFO counts
and al, bMFCofifo ; any room in the output FIFO?
cmp al, 10h ; one byte left in the FIFO?
loopz @b ; wait till there is more room
jcxz deadtx ; exit on a timeout
;
mvsendit:
mov dx, MIDIDATA ; get the data output port
xor dx,[_MVTranslateCode] ; translated I/O addr
mov al, ah
out dx, al ; ship stuff...
;
deadtx:
ret
;
send_yamaha:
;
; this byte goes out the yamaha 3802
;
mov al, 5 ; 5x
mov [MDctrl], al ; shadow it...
mov dx, MDSYSCTLR ; get the port
out dx, al ; let'er rip...
mov dx, MDGROUP4 ; 54 = FIFO-Tx status
;
@@: in al, dx
test al, TxRDY ; TxRDY bit?
jz @b ; ..jump if FIFO full
mov dl, LOW MDGROUP6 ; 56 = FIFO-Tx data
mov al, ah
out dx, al ; ship datum
ret
dosendbyte endp
;
; /*\
;---|*|----====< enable_mv101 >====----
;---|*|
;---|*| Enable the MV101 tranceiver, and any appropriate interrupts
;---|*|
;---|*| Entry Conditions:
;---|*| AH = FF to enable input interrupts
;---|*| AL = FF to enable output interrupts
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX modified.
;---|*| Carry clear -- assumed to be good
;---|*|
; \*/
;
enable_mv101 proc near
;
; build the correct mask bits in ah
;
and ax,(bMCRenafifoi SHL 8) + bMCRenafifoo
or ah,al ; ah holds the enable bits
;
; do something to the test register
;
;;; mov dx,MIDITEST
;;; xor dx,[_MVTranslateCode] ; translated I/O addr
;;; mov al,0FFh
;;; out dx,al
;
; setup the MIDI prescale
;
mov dx,MIDIPRESCALE
xor dx,[_MVTranslateCode] ; translated I/O addr
mov al,10h ;
out dx,al
;
; reset the fifos
;
mov dx,MIDICONTROL
xor dx,[_MVTranslateCode] ; translated I/O addr
mov al,bMCRrstfifoi+bMCRrstfifoo
out dx,al ; send the reset bits
mov al,ah
out dx,al ; clear reset bits and set enables
;
; reset any present status bits
;
mov dx,MIDISTATUS
xor dx,[_MVTranslateCode] ; translated I/O addr
mov al,-1 ; reset all midi status bits
out dx,al
;
; only enable the stuff if we're using interrupts
;
test [ProcessControl],INTINPUT+INTOUTPUT
jz enamv_done ; no interrupts, just exit now...
;
; install the interrupt handling now
;
cli
lea ax,MV101_ISR
call hook_interrupt ; insert our routine
sti
;
enamv_done:
clc
ret
enable_mv101 endp
;
; /*\
;---|*|----====< enable_yamaha >====----
;---|*|
;---|*| Enable the Yamaha 3802 tranceiver, and any appropriate interrupts.
;---|*|
;---|*| Enable the 3802 Yamaha MIDI device
;---|*| AH = FF to enable input interrupts.
;---|*| AL = FF to enable output interrupts.
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX modified.
;---|*| Carry clear -- assumed to be good
;---|*|
; \*/
;
enable_yamaha proc near
;
; build the correct mask bits in ah
;
and ax,(RxIRQ SHL 8)+ TxIRQ
or al,ah ; al holds the enable bits
push ax ; save the interrupt enable bits
;
; reset the 3802 midi chip
;
mov al, 80h ; master clear
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al
mov cx, 100
@@: in al, dx ; wait 32 TCLK
loop @b
xchg ax,cx ; load zero
out dx,al ; flush the reset bit
mov al, 6
mov [MDctrl], al
out dx, al ; 6x
mov dx, MDGROUP6 ; 66 = Click counter control
mov al, 00000010b ; Clock=1.0MHz, no click pulse
out dx, al
mov al, 4
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al ; 4x
mov dx, MDGROUP4 ; 44 = Tx communication rate
mov al, 00001000b ; CLKM/32 = 31,250 bps
out dx, al
mov al, 2
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al ; 2x
mov dx, MDGROUP4 ; 24 = Rx communication rate
mov al, 00001000b ; CLKM/32 = 31,250 bps
out dx, al
mov al, 5
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al ; 5x
mov dx, MDGROUP5 ; 55 = FIFO-Tx control
mov al, 10000101b ; TxE
out dx, al
mov al, 8
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al ; 8x
mov dx, MDGROUP4 ; 84 = General Timer LSB
mov ax, TIMER1
out dx, al
mov dx, MDGROUP5 ; 85 = General Timer MSB
xchg ah, al
out dx, al
;
; clear the FIFO-Rx
;
mov al, 3 ; 35 = RCR: FIFO-Rx control
mov [MDctrl], al ; bit 7 = clear FIFO-Rx
mov dx, MDSYSCTLR ; bit 6 = clear RxOV flag
out dx, al ; bit 4 = enable MIDI-clock filter
mov dx, MDGROUP5 ; bit 3 = clear BRK flag
mov al, 11001101b ; bit 2 = clear RxOL flag
out dx, al ; bit 1 = enable Address-hunter
;
; only enable the stuff if we're using interrupts
;
test [ProcessControl],INTINPUT+INTOUTPUT
jz enayam_already ; no interrupts, just exit now...
; ; bit 0 = enable Receiver
; initialize the 3802 chip IRQ system
;
sub al, al
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al ; 0x
mov dx, MDGROUP5 ; 05 = IRQ mode control
mov al, 0011b ; VE+VM
out dx, al
;
; initialize the board and motherboard IRQ systems
;
cli
.errnz MDGROUP6-MDGROUP5-1
inc dx
pop ax ; get the interrupt enable bits
out dx, al
mov [ISRenab], al
mov dx, MDGROUP4 ; flush any pending IRQs
mov al, 0ffh
out dx,al
;
; hook the actual vector
;
lea ax,Yamaha_ISR
call hook_interrupt
sti
clc
ret
;
enayam_already:
pop ax
clc
ret
enable_yamaha endp
;
; /*\
;---|*|----====< hook_interrupt >====----
;---|*|
;---|*| Hook the Pro Audio hardware interrupt,
;---|*| and enable the system interrupt.
;---|*|
;---|*| Entry Conditions:
;---|*| None.
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX modified.
;---|*|
; \*/
;
hook_interrupt proc near
push ds
push es
cmp wptr cs:[OldISR+2],0 ; any segment?
jnz midihooked ; yes, we've hooked already
push ax ; save the IRQ offset
mov ax, 3508h ; DOS : get vector
cmp [_IRQNumb],7 ; 1st IRQ channel?
jbe @F
mov al,70h-8 ; 2nd IRQs start at 70H
;
@@:
add al, [_IRQNumb] ; IRQ
push ax ; save the IRQ #
int 21h ; fetch the vector
mov wptr cs:[OldISR+0], bx ; save original value
mov wptr cs:[OldISR+2], es
pop ax
pop dx ; get the actual offset
push ds ; save ds over the int call
push cs
pop ds ; ds:dx point to the vector
mov ah, 25h ; DOS : set vector
int 21h
pop ds
;
; enable the Pro Audio IRQ channel
;
pushf
cli
;
; address the correct IRQ controller
;
mov dx,IRQ1MASKREG
cmp [_IRQNumb],7 ; 1st IRQ controller?
jbe @F ; yes, continue on...
mov dx,IRQ2MASKREG ; no, use the 2nd interrupt
;
@@:
;
; Enable the correct IRQ channel
;
in al, dx ; enable the system IRQ
mov ah, [_IRQMask]
not ah
and al, ah
out dx, al
;
; Enable the Pro Audio MIDI interrupt
;
mov dx, INTRCTLR ; Enable the PAS. This could signal
xor dx, [_MVTranslateCode]
in al, dx ; an interrupt immediately.
or al, bICmidi ; MIDI interrupt enable
out dx, al
popf
;
midihooked:
pop es
pop ds
ret
hook_interrupt endp
;
; /*\
;---|*|----====< void primexmit >====----
;---|*|
;---|*| Prime the transmitter. If interrupt driven output, make sure the
;---|*| outbound FIFO has at least one byte loaded. If polled, send
;---|*| the next polled byte.
;---|*|
;---|*| Entry Conditions:
;---|*| Working registers assumed to hold data.
;---|*|
;---|*| Exit Conditions:
;---|*| No registers modified.
;---|*|
; \*/
;
primexmit proc near
;
; save everything for the caller
;
pushf ; save the flags so we can CLI
push ax ; don't mess with any other registers
push dx
push bx
push cx
;
; Interrupt driven output is a special case. We just have to prime the pump...
;
test [ProcessControl],INTOUTPUT ; interrupt driven output?
jnz prixmints ; yes, force the xmitter to work
;
prixprimeit:
;
; This is polled output mode. We just send one byte out. The caller will keep
; calling us till the queue is emptied.
;
cmp [MIDIoQueueCnt],0 ; any data to send?
jz prixdone ; no, it's all loaded and gone..
mov bx,[lpoMOQueue] ; get the out-of-buffer pointer
mov al,[bx]
inc bx
cmp bx,offset oCircularQueue+MAXQUEUE ; wrapped?
jnz @F
mov bx,offset oCircularQueue ; yes, wrap it...
;
@@:
mov [lpoMOQueue],bx ; save the new input-to-buffer pointer
dec [MIDIoQueueCnt] ; add one more into the FIFO
call dosendbyte ; send it via the polled routines
;
prixdone:
;
; return with nothing changed
;
pop cx
pop bx
pop dx
pop ax
popf
ret
;
prixmints:
;
; in case of some MIDI interrupts occuring when we don't expect it, (such
; as MIDI in interrupts) this section will be executed without interrupts
; to avoid double sending, or sending data from the queue out of order.
;
cli ; no ints while we go to prime the pump
;
; Make sure the transmitter FIFO has data loaded. This guarrantees an interrupt
; when the FIFO is empty.
;
test [_MVHWVersionBits],bMV101; MV101 version of MIDI?
jz prixyamaha ; no, go do yamaha
mov dx, MIDIFIFOS ; get the transmitter FIFO status
xor dx,[_MVTranslateCode] ; translated I/O addr
in al,dx ; get the FIFO output free room count
and al,bMFCofifo ; isolate the outbound count
jnz prixdone ; there is data, no need to prime it.
jmp short prixprimeit ; 16 bytes free - load one more...
;
prixyamaha:
;
; Yamaha 3802 - we go to TX register set 05 to find the TX empty bit
;
mov al, 5 ; reg set 5x is for the xmitter
mov [MDctrl], al
mov dx, MDSYSCTLR
out dx, al ; 5x
mov dx, MDGROUP4 ; 84 = General Timer LSB
in al,dx
test al,TxEMP ; is the transmitter empty?
jnz prixprimeit ; yes, go load another byte
jmp short prixdone ; no, leave it alone
primexmit endp
;
; /*\
;---|*|----====< unhook_interrupt >====----
;---|*|
;---|*| If installed, disable the IRQ system, then unhook
;---|*| the Pro Audio IRQ handler from the chain.
;---|*|
;---|*| Entry Conditions:
;---|*| None.
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX may be modified.
;---|*|
; \*/
;
unhook_interrupt proc near
;
; don't do this if there is no need
;
cmp wptr cs:[OldISR+2],0 ; interrupt already unhooked?
jz unhooked ; yes, just exit
;
; disable system interrupt for the Pro Audio card
;
pushf
cli
test al,fICintmaskbits ; any other interrupts enabled
jnz mskint_done ; yes, leave the system mask alone
mov dx,IRQ1MASKREG
cmp [_IRQNumb],7 ; 1st IRQ controller?
jbe @F ; yes, continue on...
mov dx,IRQ2MASKREG ; no, use the 2nd interrupt
;
@@:
cmp [_IRQNumb],2 ; hardware IRQ 2?
jz mskint_done ; yes, never mask it
in al,dx
or al,[_IRQMask] ; no, kill the system interrupts
out dx,al
;
mskint_done:
mov dx, INTRCTLR ; disable the Pro Audio MIDI IRQ
xor dx, [_MVTranslateCode]
in al, dx
and al, not bICmidi ; MIDI interrupt disable
out dx, al
popf
;
; calculate the correct vector and restore it.
;
mov ax, 2508h ; DOS : set vector
cmp [_IRQNumb],7 ; 1st IRQ channel?
jbe @F
mov al,70h-8 ; 2nd IRQs start at 70H
;
@@:
add al, [_IRQNumb] ; IRQ
;
; do it through DOS...
;
push ds
lds dx, cs:[OldISR]
int 21h
pop ds
mov wptr cs:[OldISR+2],0 ; flush the segment of the vector
;
unhooked:
ret
unhook_interrupt endp
;
; /*\
;---|*|----====< whereirq >====----
;---|*|
;---|*| This routine queries MVSOUND.SYS for the Pro Audio IRQ. Once
;---|*| determined, then an appropriate interrupt mask will be
;---|*| generated, and saved for future reference.
;---|*|
;---|*| Entry Conditions:
;---|*| None.
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX may be modified.
;---|*|
; \*/
;
whereirq proc near
;
; Do this just once
;
cmp [_IRQNumb],-1 ; have we been through here?
jnz IRQdone ; yes, it's been found
;
; Call MVSOUND.SYS to get the DMA/IRQ
;
mov ax,0bc04h ; call MVSOUND.SYS for it...
int 2fh
;
; register the IRQ if the call was good
;
cmp ax,'MV' ; was this a good call?
jnz IRQdone
mov [_IRQNumb],cl ; save the #
mov ax,1
shl ax,cl
or al,ah
mov [_IRQMask],al ; save the mask.
;
IRQdone:
ret
whereirq endp
;
;------------------------===================================-------------------
;------------------------====< MIDI Interrupt Service > ====-------------------
;------------------------===================================-------------------
;
;
; /*\
;---|*|----====< MV101_ISR >====----
;---|*|
;---|*| ISR service routine for the MV101 MIDI interrupts.
;---|*|
; \*/
;
MV101_ISR proc far
push ds
push ax
push bx
push cx
push dx
mov ax,@data
mov ds,ax
;
; first time in, check for a MIDI interrupt
;
mov dx,INTRCTLRST
xor dx,[_MVTranslateCode] ; translate the address
in al,dx ; get the interrupt status
test al,bISmidi ; is this our interrupt?
jnz mv_is_midi_int ; yes, go process it...
test al,fISints ; are any of these interrupt legit?
jjz midiint_done ; no, flush this int.
;
; this was not our interrupt, chain on...
;
pop dx
pop cx
pop bx
pop ax
pop ds
jmp dword ptr cs:[OldISR] ; pass off to the other code.
;
mv_is_midi_int:
;
; check the receiver for data, or errors
;
mov dx,MIDISTATUS
xor dx,[_MVTranslateCode] ; translate the address
in al,dx
mov ah,al ; save a copy for xmitter
and al,bMSRframeerr+bMSRififoovr+bMSRofifoovr ; overrun or frame error?
jz mvmidi_int ; nope
out dx,al ; clear errors
mov dx,MIDICONTROL
xor dx,[_MVTranslateCode] ; translate the address
in al,dx
or al,bMCRrstfifoi+bMCRrstfifoo ; reset FIFO in ptr
out dx,al
or al,NOT(bMCRrstfifoi+bMCRrstfifoo)
out dx,al
;
mvmidi_int:
;
; get the status once again for the second chance loop
;
mov dx,MIDISTATUS
xor dx,[_MVTranslateCode] ; translate the address
in al,dx
;
; Only process the interrupt process wanted by the user
;
test [ProcessControl],INTINPUT ; interrupt driven input?
jz checktrans ; no, go check for int driven xmit
test al,bMSRififo ; any data in input FIFO?
jz checktrans ; no, go check the transmitter
.errnz MIDIFIFOS-MIDISTATUS-1
inc dx ; move to the status register
in al,dx
mov cl,al
and cx,bMFCififo ; get # of bytes in FIFO
jnz @F
mov cx,16
;
@@:
mov dx,MIDIDATA ; move to the data register
xor dx,[_MVTranslateCode] ; translate the address
mov bx,[lpiMIQueue] ; get the input queue pointer
;
mvrxloop:
;
; read the data off the receiver
;
in al,dx ; fetch the received byte
;
; MIDI spec requires transmitter send something every 300 msec
; Active Sense (FE) is the standard "nothing for you" byte
;
test [_MidiInFilter],MF_ACTSENSE ; filter out active sense
jz @F ; no, keep it...
cmp al, 0FEh ; Active Sense?
je rxCont ; ..ignore it
;
@@:
cmp [MIDIiQueueCnt],MAXQUEUE; is it full?
jle @F ; no, save it
inc [_overflow] ; yes, list it as an error
jmp short mvrxloop
;
@@:
mov [bx],al
inc bx
inc [MIDIiQueueCnt] ; one more in...
cmp bx,offset iCircularQueue+MAXQUEUE
jb rxCont
mov bx,offset iCircularQueue
;
rxCont:
loop mvrxloop ; do all bytes listed in the queue
mov [lpiMIQueue],bx ; save the input queue pointer
jmp second_check ; make sure FIFO is empty
;
checktrans:
;
; If we are using interrupt driven output, load the FIFO now...
;
test [ProcessControl],INTOUTPUT; interrupt driven output?
jz second_check ; no, just exit out
; reset the interrupt now.
mov dx,MIDISTATUS
xor dx,[_MVTranslateCode] ; translate the address
mov al,bMSRofifo ; any data in output FIFO?
out dx,al
; check for any outgoing data
cmp [MIDIoQueueCnt],0 ; any queued up data?
jz second_check ; no, just exit out
mov dx,MIDIFIFOS
xor dx,[_MVTranslateCode] ; translate the address
in al,dx
and ax,bMFCofifo ; get # of outgoing bytes
mov cl,4
shr al,cl ; right hand justify the number
xchg ax,cx
cmp cl,1 ; down to 1 byte free?
je second_check ; yes, can't load it
ja @F ; we have 15 or less entries
mov cx,16 ; 16 entires fit, but...
;
@@:
dec cx ; we only load 15...
mov dx,MIDIDATA ; move to the data register
xor dx,[_MVTranslateCode] ; translate the address
mov bx,[lpoMOQueue] ; get the input queue pointer
;
txLoop:
mov al,[bx] ; pass the next byte
out dx,al
inc bx ; move the pointer
cmp bx,offset oCircularQueue+MAXQUEUE
jb @F
mov bx,offset oCircularQueue; it wrapped...
;
@@:
dec [MIDIoQueueCnt] ; any queued up data?
loopnz txLoop
mov [lpoMOQueue],bx
;
; all done with input and output processing. Check for more interrupts
;
second_check:
;
; in case we received more MIDI data during this routine, check again...
;
mov dx,INTRCTLRST
xor dx,[_MVTranslateCode] ; translate the address
in al,dx ; get the interrupt status
test al,bISmidi ; is the interrupt still active?
jjnz mvmidi_int ; yes, go process it...
;
midiint_done:
mov al,EOI ; acknowledge the interrupt.
cmp [_IRQNumb],7
jbe @F
out IRQ2ACKREG, al ; ack the 2nd IRQ controller
;
@@:
out IRQ1ACKREG, al ; ack the 1st IRQ controller
ToggleMV101mask ; make sure the line stays active
;
busy_exit:
pop dx
pop cx
pop bx
pop ax
pop ds
iret
MV101_ISR endp
;
; /*\
;---|*|----====< Yamaha_ISR >====----
;---|*|
;---|*| ISR service routine for the Yamaha 3802 interrupts.
;---|*|
; \*/
;
Yamaha_ISR proc far
push ds ; minimal save
push dx
push ax
mov ax, @data ; establish our DS
mov ds, ax
mov dx, INTRCTLRST
in al, dx ; acknowledge interrupt
test al, bICmidi ; our MIDI interrupt?
jnz isourint ; yes, go do it...
pushf
call dword ptr cs:[OldISR] ; perform the old interrupt
jmp WOOPS_exit
;
isourint:
;
; we can now process all MIDI interrupts here
;
push es
push di
cld ; fatal to assume
;
; We will only process interrupt generated input if ProcessControl says so.
;
test [ProcessControl],INTINPUT ; Int input?
jz emptyRX ; polled, check each h/w
;
; Check for Rx IRQ (means FIFO-Rx went non-empty) empty the FIFO
;
mov dx, MDSYSSTAT ; IRQ Status
in al, dx
and al, RxIRQ ; FIFO-RX non-empty?
jz emptyRX ; ..jump if not
.errnz MDIRQCLR-MDSYSSTAT-1
inc dx
out dx,al ; flush it...
;
; get the input queue & load'er up!
;
mov ax,ds
mov es,ax
mov di,lpiMIQueue ; get the input to the queue
;
rxISR_05:
mov al, 3 ; 3x
mov dx, MDSYSCTLR
out dx, al
mov dx, MDGROUP4 ; 34 = FIFO-Rx status
in al, dx
test al, RxRDY ; RxRDY bit?
jz rxISR_10 ; ..jump if FIFO empty
mov dx, MDGROUP6 ; 36 = FIFO-Rx data
in al, dx ; fetch datum
;
; Eliminate some overhead! The MIDI spec requires transmitter send something
; every 300 msec. Active Sense (FE) is the standard "nothing for you" byte
;
test [_MidiInFilter],MF_ACTSENSE ; filter out active sense
jz @F ; no, keep it...
cmp al, ACTV_SENSE ; Active Sense?
je rxISR_05 ; ..ignore it
;
@@:
cmp [MIDIiQueueCnt],MAXQUEUE; is it full?
jle @F ; no, save it
inc _overflow
jmp short rxISR_05
;
@@:
stosb
inc [MIDIiQueueCnt] ; one more in...
cmp di,offset iCircularQueue+MAXQUEUE
jb rxISR_05
mov di,offset iCircularQueue
jmp rxISR_05
;
rxISR_10:
mov [lpiMIQueue],di ; get the input to the queue
;
emptyRX:
;
; Check for Transmitter Empyt IRQ here
;
test [ProcessControl],INTOUTPUT ; Int output?
jz endMIDIint ; no, exit out...
mov dx, MDSYSSTAT ; IRQ Status
in al, dx
and al, TxIRQ ; FIFO-Tx became empty?
jz endMIDIint ; ..jump if not
.errnz MDIRQCLR-MDSYSSTAT-1
inc dx
out dx,al ; flush it...
;
; output any data till done.
;
cmp [MIDIoQueueCnt],0 ; any data in the queue?
jz endMIDIint ; ..jump if not
mov di,lpoMOQueue ; get the pointer to the queue
mov dx, MDSYSCTLR ; index to Xmitter
mov al,5
out dx,al
;
ytxLoop:
mov dx, MDGROUP4 ; 54 = FIFO-Tx status
in al, dx
test al, TxRDY ; TxRDY bit?
jz ytxDone ; ..jump if FIFO full
mov dx, MDGROUP6 ; 56 = FIFO-Tx data
mov al, [di]
out dx, al ; ship datum
inc di ; move the pointer
cmp di,offset oCircularQueue+MAXQUEUE
jb @F
mov di,offset oCircularQueue; it wrapped...
;
@@:
dec [MIDIoQueueCnt] ; any queued up data?
jnz ytxLoop ; do as much as possible
;
ytxDone:
mov [lpoMOQueue],di ; save the ending pointer
;
endMIDIint:
;
; all int handling is done now, so restore the state & exit home...
;
pop di
pop es
mov al, [MDctrl]
mov dx, MDSYSCTLR
out dx, al ; restore incoming state
;
WOOPS_reenter:
;
; flush the data ready IRQ
;
mov al,EOI ; acknowledge at the system level
cmp _IRQNumb,7
jbe @F
out IRQ2ACKREG, al ; ack the 2nd IRQ controller
;
@@:
out IRQ1ACKREG, al ; ack the 1st IRQ controller
ToggleMV101mask ; make sure the MV101 can generate ints
;
WOOPS_exit:
pop ax
pop dx
pop ds
iret
Yamaha_ISR endp
; /*\
;---|*| end of MIDIA.ASM
; \*/
end